#include "Bomba.h"
#include "../FabricasObjetosJuego.h"
#include <iostream>
#include <algorithm>
#include <math.h>

const int EstadoBomba::Activado = 0;
const int EstadoBomba::Explotando = 1;
const int EstadoBomba::SinParticular = 2;
const vector<int> EstadoBomba::acciones = make_vector<int>() << EstadoBasico::Movimiento;
const vector<string> EstadoBomba::descripcionAcciones = make_vector<string>() << "mover";
const vector<int> EstadoBomba::accionesParticulares = make_vector<int>() << EstadoBomba::Activado << EstadoBomba::Explotando;
const vector<string> EstadoBomba::descripcionAccionesParticulares = make_vector<string>() << "activar" << "explotar";

Bomba::Bomba (int x, int y, int maximoX, int maximoY, Orientacion orientacion) : ObjetoMovible(x, y, maximoX, maximoY, orientacion) {

	if (orientacion == Derecha) estadoDerecha = Empezo;
	else estadoIzquierda = Empezo;

	estado = EstadoBasico::Movimiento;
	estadoParticular = EstadoBomba::SinParticular;
	prioridad = 4;
	tipo = ENEMIGO;
	tiempoActivacion = 0;
	distanciaMinima = 100000;
	alturaEscalera = 0;
	subirEscalera = false;
	bajarEscalera = false;
	enEscalera = false;
	detenido = false;
	activarDetonacion = false;
	sumo = false;
	srand( time(NULL) );
}

void Bomba::MoverArriba(Uint32 deltaTime) {
	setPosicion(getX(), getY() - velocidad*deltaTime/1000);
}

void Bomba::MoverAbajo(int maximoY, Uint32 deltaTime) {
	setPosicion(getX(), getY() + velocidad*deltaTime/1000);
}

void Bomba::setEnEscalera(bool is) {
	enEscalera = is;
}

void Bomba::setAlturaEscalera(int altura) {
	alturaEscalera = altura;
}

void Bomba::caer(Uint32 deltaTime, int maximoY) {
	if (enEscalera) return;
	else ObjetoMovible::caer(deltaTime, maximoY);
}

void Bomba::Actualizar(Uint32 time) {

	if (getEstadoParticular() == EstadoBomba::SinParticular && !activarDetonacion) tiempoActivacion = time;
	Activar(time);

	if (enEscalera) {
		if (subirEscalera) {
			MoverArriba(time - lastTimeActualizar);
			if (getY() + alto < alturaEscalera) {
				setPosicion(getX(), alturaEscalera - alto);
				enEscalera = false;
			}
			return;
		} else if (bajarEscalera) {
			MoverAbajo(maximoY, time - lastTimeActualizar);
			if (getPosicion().getY() + getAlto() > getPiso()) {
				setPosicion(getPosicion().getX(), getPiso() - getAlto());
				enEscalera = false;
			}
			return;
		}
	}

	setEnEscalera(false);

	if (estadoDerecha == Moviendo) {
		MoverDerecha(maximoX, time - lastTimeActualizar);
		if (getX() > maximoX) {
			setEstadoParticular(EstadoBomba::Explotando);
			setActivo(false);
		}
	}

	if (estadoIzquierda == Moviendo) {
		MoverIzquierda(time - lastTimeActualizar);
		if (getX() + ancho < 0){
			setEstadoParticular(EstadoBomba::Explotando);
			setActivo(false);
		}
	}

	if (estadoDerecha == Empezo) {
		estadoDerecha = Moviendo;
		setEstado(EstadoBasico::Movimiento);
	}

	if (estadoIzquierda == Empezo) {
		estadoIzquierda = Moviendo;
		setEstado(EstadoBasico::Movimiento);
	}
}

void Bomba::Detener() {
	estadoDerecha = SinMovimiento;
	estadoIzquierda = SinMovimiento;
	detenido = true;
}

pair<idImagen_t, int> Bomba::getDibujable(Uint32 time) {

	pair<idImagen_t, int> dibujable;

	if (estadoParticular == EstadoBomba::SinParticular) {
		dibujable = ObjetoMovible::getDibujable(time);
	}
	else if (estadoParticular == EstadoBomba::Activado) {
		bool status = true;
		dibujable = getDibujableParticular(accionesParticulares.at(estadoParticular), time, &status);
	}
	else {
		bool status = true;
		dibujable = getDibujableParticular(accionesParticulares.at(estadoParticular), time, &status);
		if (!status) {
			estadoVida = Muerto;
			if (rand() % 100 > 60 ) {
				FabricaBonus::CrearObjeto(getX(), getY(), orientacion);
			}
		}
	}

	return dibujable;
}

void Bomba::addAccionParticular(int idImagen, int cuadros) {
	Animacion *animacion = new Animacion (cuadros, 0, idImagen, ancho, alto);
    accionesParticulares.push_back(animacion);
}

int Bomba::getEstadoParticular(){
	return estadoParticular;
}

void Bomba::setEstadoParticular(int estado) {
	estadoParticular = estado;
}

int Bomba::indexEstado(int estado) {

	vector<int> estados = EstadoBomba::acciones;

	unsigned int posicion = find(estados.begin(), estados.end(), estado) - estados.begin();

	if (posicion >= estados.size())
		return -1;

	return posicion;
}

void Bomba::Activar(Uint32 time) {
	if (getEstadoParticular() == EstadoBomba::Explotando) return;

	if (getEstadoParticular() == EstadoBomba::SinParticular && activarDetonacion) {
		setEstadoParticular(EstadoBomba::Activado);
		tiempoActivacion = time;
	}
	else if (time - tiempoActivacion >= TIEMPO_DETONACION) {
		Explotar(v_personajes);
	}
	DetectarCercania();
	Perseguir();
}

void Bomba::Perseguir() {

	if (getEstadoParticular() == EstadoBomba::Explotando || detenido) return;

	Punto ptoThis (getX() + getAncho()/2, getY()+getAlto()/2);
	ObjetoMovible* perDistanciaMin;

	perDistanciaMin = v_personajes.at(0);
	Punto ptoPer (v_personajes.at(0)->getX() + v_personajes.at(0)->getAncho()/2, v_personajes.at(0)->getY() + v_personajes.at(0)->getAlto()/2);
	distanciaMinima = sqrt(((ptoPer.getX()-ptoThis.getX())*(ptoPer.getX()-ptoThis.getX())) + ((ptoPer.getY()-ptoThis.getY())*(ptoPer.getY()-ptoThis.getY())));

	for (unsigned int i=1; i<v_personajes.size(); i++) {
		Punto ptoPer (v_personajes.at(i)->getX() + v_personajes.at(i)->getAncho()/2, v_personajes.at(i)->getY() + v_personajes.at(i)->getAlto()/2);
		if (int dist = sqrt(((ptoPer.getX()-ptoThis.getX())*(ptoPer.getX()-ptoThis.getX())) + ((ptoPer.getY()-ptoThis.getY())*(ptoPer.getY()-ptoThis.getY()))) < distanciaMinima) {
			perDistanciaMin = v_personajes.at(i);
			distanciaMinima = dist;
		}
	}
	if ((perDistanciaMin->getX()+perDistanciaMin->getAncho())/2 > (getX()+getAncho())/2) {
		estadoDerecha = Moviendo;
		estadoIzquierda = SinMovimiento;
	}
	else if ((perDistanciaMin->getX()+perDistanciaMin->getAncho())/2 <= (getX()+getAncho())/2) {
		estadoDerecha = SinMovimiento;
		estadoIzquierda = Moviendo;
	}

	if (getY()+getAlto() < perDistanciaMin->getY()+perDistanciaMin->getAlto()) {
		subirEscalera = false;
		bajarEscalera = true;
	}
	else if (getY()+getAlto() > perDistanciaMin->getY()+perDistanciaMin->getAlto()) {
		subirEscalera = true;
		bajarEscalera = false;
	}
	else {
		subirEscalera = false;
		bajarEscalera = false;
	}
}

void Bomba::DetectarCercania() {
	if (getEstadoParticular() == EstadoBomba::Explotando || getEstadoParticular() == EstadoBomba::Activado) return;

	Punto ptoThis (getX() + getAncho()/2, getY()+getAlto()/2);
	for (unsigned int i=0; i<v_personajes.size(); i++) {
		Punto ptoPer (v_personajes.at(i)->getX() + v_personajes.at(i)->getAncho()/2, v_personajes.at(i)->getY() + v_personajes.at(i)->getAlto()/2);
		if (sqrt(((ptoPer.getX()-ptoThis.getX())*(ptoPer.getX()-ptoThis.getX())) + ((ptoPer.getY()-ptoThis.getY())*(ptoPer.getY()-ptoThis.getY()))) < RADIO_ALCANCE) {
			PutOnDetone();
		}
	}
}

void Bomba::PutOnDetone() {
	activarDetonacion = true;
}

void Bomba::Explotar(vector<ObjetoMovible*> v_personajes) {

	if (getEstadoParticular() == EstadoBomba::Explotando) return;

	Punto ptoThis (getX() + getAncho()/2, getY()+getAlto()/2);

	for (unsigned int i=0; i<v_personajes.size(); i++) {
		Punto ptoPer (v_personajes.at(i)->getX() + v_personajes.at(i)->getAncho()/2, v_personajes.at(i)->getY() + v_personajes.at(i)->getAlto()/2);
		int distancia = sqrt((ptoPer.getX()-ptoThis.getX())*(ptoPer.getX()-ptoThis.getX()) + (ptoPer.getY()-ptoThis.getY())*(ptoPer.getY()-ptoThis.getY()));

		if (distancia <= RADIO_ALCANCE) {
			static_cast<Personaje*> (v_personajes.at(i))->RestarPuntos(PUNTOS_RESTAR);
			static_cast<Personaje*> (v_personajes.at(i))->restarVida();
		}
	}
	setEstadoParticular(EstadoBomba::Explotando);
}

void Bomba::Morir() {
	Explotar(v_personajes);
}

void Bomba::ColisionarConPersonaje(ObjetoEstatico* personaje, int colX, int colY, int colXvieja, int colYvieja) {

	Personaje* per = dynamic_cast<Personaje*> (personaje);

	if (getEstadoParticular() == EstadoBomba::Explotando) return;

	if (colX > colY) {
		if (per->getVelocidadY() < 0) {
			if (getEstadoParticular() == EstadoBomba::Activado) {
				per->setPosicion(per->getX(), getY() - per->getAlto());
				Detener();
				if (!sumo) {
					sumo = true;
					per->SumarPuntos(PUNTOS_SUMAR);
				}
				return;
			}
			Detener();
			PutOnDetone();
			per->setVelocidadY(120);
			per->setPosicion(per->getX(), getY() - per->getAlto());
		} else {
			per->setVelocidadY(per->getVelocidadY() * (-1));
			per->setPosicion(per->getX(), getY() + getAlto());
		}
	} else {
		if (orientacion == Derecha) setPosicion(per->getX() - getAncho(), getY());
		else setPosicion(per->getX() + per->getAncho(), getY());
	}
}

void Bomba::ColisionarConEnemigo(ObjetoEstatico* movible, int colX, int colY, int colXvieja, int colYvieja) {

	ObjetoMovible* mov = dynamic_cast<ObjetoMovible*> (movible);

	if (getPrioridad() < mov->getPrioridad()) mov->ColisionarConEnemigo(this, colX, colY, colXvieja, colYvieja);

	if (getEstadoParticular() == EstadoBomba::Explotando) return;

	else {
		if (colXvieja != 0) {
			if (mov->getVelocidadY() <= 0) movible->setPosicion(movible->getX(), getY() - movible->getAlto());
			else movible->setPosicion(movible->getX(), getY() + getAlto());
		}
		else if (colYvieja != 0) return;
		else {
			if (colX >= colY) {
				if (mov->getVelocidadY() <= 0) movible->setPosicion(movible->getX(), getY() - movible->getAlto());
				else movible->setPosicion(movible->getX(), getY() + getAlto());
			}
			else return; //mov->CambiarSentido(getX(), getAncho());
		}
	}
}
